package com.dq.wx.pay.async;

import com.dq.controller.in.SubscribePayRequest;
import com.dq.domain.subscribe.Subscribe;
import com.dq.domain.sys.user.Account;
import com.dq.repository.account.AccountRepository;
import com.dq.repository.user.SubscribeRepository;
import com.dq.service.OssService;
import com.dq.service.subscribe.SubscribeService;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

@Component
public class WxPayAsyncMethod {
    private static final Logger log = LoggerFactory.getLogger(WxPayAsyncMethod.class);

    @Autowired
    private SubscribeRepository subscribeRepository;
    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private Redisson redisson;
    @Autowired
    private OssService ossService;
    @Autowired
    private SubscribeService subscribeService;

    @Async
    /**
     * 支付回调通知处理, 异步执行业务逻辑
     * @param outTradeNo 商户订单号，与请求一致, 自己生成
     * @param transactionId 微信/支付宝支付订单号, 微信/支付宝生成 TODO 可以存到订单表中
     * @param totalFee 订单总金额，单位为分, 注意支付宝下单API 传值为元
     * @param payTime 微信/支付宝 返回的 支付完成时间, long: 毫秒
     * @param sign 微信/支付宝 返回的 sign, TODO 调用统一下单接口时, 需保存sign 到订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void parseOrderNotifyResultAsync(String outTradeNo, String transactionId, Integer totalFee, Long payTime, String sign) {
        /// 多服务器 需使用分布式锁, 单服务器而处理业务逻辑采用 异步方式, 也需要使用分布式锁
        // 加锁: 采用数据锁进行并发控制，以避免函数重入造成的数据混乱
        String lockName = "wx_notify_order_" + outTradeNo;    // 每个订单对应一个锁
        RLock rLock = redisson.getLock(lockName);
        // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
        rLock.lock(300, TimeUnit.SECONDS);
        try {
//            synchronized (this) {
            /// 应结订单金额=订单金额-非充值代金券金额，应结订单金额<=订单金额
//                Integer settlementTotalFee = notifyResult.getSettlementTotalFee();


            // 根据订单号, 获取订单信息
            Subscribe subscribe = subscribeRepository.findByDeletedFalseAndSubscribeNo(outTradeNo);
            if (subscribe == null) {
                throw new RuntimeException("订单不存在, orderNo: " + outTradeNo);
            }
            // 001: 判断订单是否支付过, 避免重复逻辑
            if (subscribe.getIsPay() != null && subscribe.getIsPay()) {
                throw new RuntimeException("订单已经支付过");
            }
            /// 002: 验证 sign TODO 验证异常
//                if (!sign.equals(orders.getSign())) {
//                    throw new RuntimeException("sign验证失败");
//                }
            // 003: 验证 金额
            if(subscribe.getServiceProject().getProjectPrice() == null) {
                throw new RuntimeException("订单金额为空");
            }
            Integer price = subscribe.getServiceProject().getProjectPrice().multiply(new BigDecimal("100")).intValue();
            if (!totalFee.equals(price)) {
                throw new RuntimeException("支付金额不对");
            }
            // 004: 删除 oss 服务器上, 扫码支付二维码(扫码有效期只为2小时)
            ossService.delete(subscribe.getQrOssUrl());
            // 005: TODO 执行其他业务逻辑
            SubscribePayRequest subscribePayRequest = new SubscribePayRequest();
            subscribePayRequest.setSubscribeId(subscribe.getId());
            subscribePayRequest.setFlag(1L);
            subscribePayRequest.setPrice(subscribe.getServiceProject().getProjectPrice());
            BigDecimal expendRatio = new BigDecimal("0");
            if(subscribe.getTechnicianUser().getExpendRatio() != null) {
                expendRatio = subscribe.getTechnicianUser().getExpendRatio();
            }
            subscribePayRequest.setProportion(expendRatio.toString());
            subscribeService.paySubscribe(subscribePayRequest);

            // 006: 修改订单状态
            subscribe.setIsPay(true);
            subscribe.setPayTime(payTime);
            subscribeRepository.save(subscribe);

            log.info("微信 支付回调通知处理 [自身业务逻辑处理完后]");
//            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            // 释放锁, 需要unlock前的判断
            // 解决异常: java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 2e9e88ca-a4e0-4c53-be05-98ed6a96ba83 thread-id: 62 http-nio-8080-exec-6----3.004
            if (rLock.isLocked()) {
                if (rLock.isHeldByCurrentThread()) {
                    rLock.unlock();
                }
            }
        }
    }

    @Async
    @Transactional(rollbackFor = Exception.class)
    public void parseOrderNotifyResultAsync02(String outTradeNo, String transactionId, Integer totalFee, Long payTime, String sign) {
        /// 多服务器 需使用分布式锁, 单服务器而处理业务逻辑采用 异步方式, 也需要使用分布式锁
        // 加锁: 采用数据锁进行并发控制，以避免函数重入造成的数据混乱
        String lockName = "wx_notify_order_02_" + outTradeNo;    // 每个订单对应一个锁
        RLock rLock = redisson.getLock(lockName);
        // 2. 支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
        rLock.lock(300, TimeUnit.SECONDS);
        try {
            // 根据订单号, 获取订单信息
            Account account = accountRepository.findByDeletedFalseAndOrderNo(outTradeNo);
            if (account == null) {
                throw new RuntimeException("订单不存在, orderNo: " + outTradeNo);
            }
            // 001: 判断订单是否支付过, 避免重复逻辑
            if (account.getPayStatus() != null && account.getPayStatus().toString().equals(Account.PayStatus.success.toString())) {
                throw new RuntimeException("订单已经支付过");
            }
            /// 002: 验证 sign TODO 验证异常
//                if (!sign.equals(orders.getSign())) {
//                    throw new RuntimeException("sign验证失败");
//                }
            // 003: 验证 金额
            if(account.getPrice() == null) {
                throw new RuntimeException("订单金额为空");
            }
            Integer price = account.getPrice().multiply(new BigDecimal("100")).intValue();
            if (!totalFee.equals(price)) {
                throw new RuntimeException("支付金额不对");
            }
            // 004: 删除 oss 服务器上, 扫码支付二维码(扫码有效期只为2小时)
            ossService.delete(account.getQrOssUrl());
            // 005: TODO 执行其他业务逻辑

            // 006: 修改订单状态
            account.setPayStatus(Account.PayStatus.success);
            account.setPayTime(payTime);
            accountRepository.save(account);

            log.info("微信 支付回调通知处理 [充值 - 自身业务逻辑处理完后]");
        }catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            // 释放锁, 需要unlock前的判断
            // 解决异常: java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 2e9e88ca-a4e0-4c53-be05-98ed6a96ba83 thread-id: 62 http-nio-8080-exec-6----3.004
            if (rLock.isLocked()) {
                if (rLock.isHeldByCurrentThread()) {
                    rLock.unlock();
                }
            }
        }
    }

}
